/*
 * XXX
 */

package edu.uhcl.csci4838.group3.pendingIntents.ringer;

import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.List;

import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import edu.uhcl.csci4838.group3.database.BackpackEduDatabaseHelper;
import edu.uhcl.csci4838.group3.database.BackpackEduDatabaseHelper.CursorCourse;
import edu.uhcl.csci4838.group3.database.BackpackEduDatabaseHelper.CursorCourseTimeOverride;
import edu.uhcl.csci4838.group3.database.BackpackEduDatabaseHelper.CursorHoliday;
import edu.uhcl.csci4838.group3.database.BackpackEduDatabaseHelper.CursorSemester;
import edu.uhcl.csci4838.group3.database.info.Course;
import edu.uhcl.csci4838.group3.database.info.CourseTimeOverride;
import edu.uhcl.csci4838.group3.database.info.Holiday;
import edu.uhcl.csci4838.group3.database.info.Semester;
import edu.uhcl.csci4838.group3.pendingIntents.AllIntents;
import edu.uhcl.csci4838.group3.pendingIntents.IntentExtraStringStorage;
import edu.uhcl.csci4838.group3.pendingIntents.OnAlarmReceiver;

/**
 * this sets up the intents for changing the ringer time.
 * 
 * @author Tyler Hays
 */
public class RingerIntent {
	
	/**
	 * Context all wide
	 */
	private final Context mContext;
	/**
	 * used for debugging
	 */
	private static final String TAG = "RingerIntent";
	/**
	 * AlarmManager
	 */
	private final AlarmManager mAlarmManager;
	
	/**
	 * set up RingerIntent
	 * 
	 * @param context
	 *            context being passed
	 */
	public RingerIntent(final Context context) {
		this.mContext = context;
		this.mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
	}
	
	/**
	 * cancel ringer for a class
	 * 
	 * @param courseCursor
	 *            cancel the ringer change for a class
	 */
	public void cancelRingChange(final CursorCourse courseCursor) {
		final Long courseStartDate = courseCursor.getColStartDate();
		final Long courseEndDate = courseCursor.getColEndDate();
		
		final Integer startTimeHour = courseCursor.getColStartTimeHour();
		final Integer startTimeMinute = courseCursor.getColStartTimeMinute();
		final Integer endTimeHour = courseCursor.getColEndTimeHour();
		final Integer endTimeMinute = courseCursor.getColEndTimeMinute();
		
		new GregorianCalendar();
		
		// checkings to make sure nothing is missing if so just leaves
		if ((courseStartDate == null)
				|| (courseEndDate == null)
				|| (startTimeHour == null)
				|| (startTimeMinute == null)
				|| (endTimeHour == null)
				|| (endTimeMinute == null)) {
			Log.e(RingerIntent.TAG,
					"Missing data");
			Log.e(RingerIntent.TAG,
					courseCursor.getColName());
			return;
		}
		
		// checking to make sure course has not ended yet
		if (courseEndDate.longValue() < Calendar.getInstance()
				.getTimeInMillis()) {
			return;
		}
		this.cancelRingerChange(courseStartDate,
				courseEndDate,
				startTimeHour,
				startTimeMinute,
				endTimeHour,
				endTimeMinute,
				courseCursor);
	}
	
	/**
	 * Sets up the ringer change for a class
	 * 
	 * @param courseCursor
	 *            cursor for the course that needs the ringer change set up
	 */
	public void setUpRingerChange(final CursorCourse courseCursor) {
		final Long courseStartDate = courseCursor.getColStartDate();
		final Long courseEndDate = courseCursor.getColEndDate();
		
		final Integer startTimeHour = courseCursor.getColStartTimeHour();
		final Integer startTimeMinute = courseCursor.getColStartTimeMinute();
		final Integer endTimeHour = courseCursor.getColEndTimeHour();
		final Integer endTimeMinute = courseCursor.getColEndTimeMinute();
		
		new GregorianCalendar();
		
		// checkings to make sure nothing is missing if so just leaves
		if ((courseStartDate == null)
				|| (courseEndDate == null)
				|| (startTimeHour == null)
				|| (startTimeMinute == null)
				|| (endTimeHour == null)
				|| (endTimeMinute == null)) {
			Log.e(RingerIntent.TAG,
					"Missing data");
			Log.e(RingerIntent.TAG,
					courseCursor.getColName());
			return;
		}
		
		// checking to make sure course has not ended yet
		if (courseEndDate.longValue() < Calendar.getInstance()
				.getTimeInMillis()) {
			Log.e(RingerIntent.TAG,
					"never starting");
			return;
			
		}
		this.setUpRingerChange(courseStartDate,
				courseEndDate,
				startTimeHour,
				startTimeMinute,
				endTimeHour,
				endTimeMinute,
				courseCursor);
		
	}
	
	/**
	 * cancel the ringer change
	 * 
	 * @param courseStartDate
	 *            start date of class
	 * @param courseEndDate
	 *            end date of class
	 * @param startTimeHour
	 *            start time hour
	 * @param startTimeMinute
	 *            start time Minute
	 * @param endTimeHour
	 *            end time Hour
	 * @param endTimeMinute
	 *            end time Minute
	 * @param courseCursor
	 *            course cursor
	 */
	private void cancelRingerChange(final Long courseStartDate,
			final Long courseEndDate,
			final Integer startTimeHour,
			final Integer startTimeMinute,
			final Integer endTimeHour,
			final Integer endTimeMinute,
			final CursorCourse courseCursor) {
		
		final Calendar when = Calendar.getInstance();
		final GregorianCalendar gcal = new GregorianCalendar();
		final ContentValues contentValues = new ContentValues();
		final BackpackEduDatabaseHelper dbhelper = new BackpackEduDatabaseHelper(this.mContext);
		CursorSemester semesterCursor;
		CursorHoliday holidayCursor;
		CursorCourseTimeOverride overrideCursor;
		final int dayBitField = courseCursor.getColDayBitField();
		final List<Integer> daysOfClass = AllIntents.setMeetingDays(dayBitField);
		final long courseID = courseCursor.getColId();
		
		contentValues.put(Semester.ID,
				Long.valueOf(courseCursor.getColSemesterId()));
		semesterCursor = dbhelper.getSemester(contentValues,
				null,
				true);
		semesterCursor.moveToFirst();
		
		holidayCursor = dbhelper.getHoliday(contentValues,
				null,
				true);
		holidayCursor.moveToFirst();
		
		contentValues.put(Course.ID,
				Long.valueOf(courseCursor.getColId()));
		overrideCursor = dbhelper.getCourseTimeOverride(contentValues,
				null,
				true);
		
		gcal.set(gcal.get(Calendar.YEAR),
				gcal.get(Calendar.MONTH),
				gcal.get(Calendar.DAY_OF_MONTH));
		
		// checks to see if the current time is before the start date of the
		// class
		if (gcal.getTimeInMillis() < courseStartDate.longValue()) {
			gcal.setTimeInMillis(courseStartDate.longValue());
		}
		
		/*
		 * This loop goes threw and finds the next class in the future that class is on.
		 */
		this.cancelRingerIntentDays(courseEndDate,
				startTimeHour,
				startTimeMinute,
				endTimeHour,
				endTimeMinute,
				when,
				gcal,
				daysOfClass,
				courseID);
		
		semesterCursor.close();
		holidayCursor.close();
		overrideCursor.close();
		dbhelper.close();
		return;
	}
	
	/**
	 * Loops threw and gets the extact day to set up intent on.
	 * 
	 * @param courseEndDate
	 *            last day of class
	 * @param startTimeHour
	 *            start time hour
	 * @param startTimeMinute
	 *            start time minute
	 * @param endTimeHour
	 *            end time hour
	 * @param endTimeMinute
	 *            end time in minute
	 * @param when
	 *            when the class starts
	 * @param gcal
	 *            calander
	 * @param daysOfClass
	 *            days the class is in sestion
	 * @param courseID
	 *            course ID
	 */
	private void cancelRingerIntentDays(final Long courseEndDate,
			final Integer startTimeHour,
			final Integer startTimeMinute,
			final Integer endTimeHour,
			final Integer endTimeMinute,
			final Calendar when,
			final GregorianCalendar gcal,
			final List<Integer> daysOfClass,
			final long courseID) {
		
		PendingIntent pi;
		while (gcal.getTimeInMillis() < courseEndDate.longValue()) {
			
			// if the class is on a given day it sets everything up and then
			// checks
			// to see if the alert is before it is currently set up.
			if (daysOfClass.contains(Integer.valueOf(gcal.get(Calendar.DAY_OF_WEEK)))) {
				// Log.d(RingerIntent.TAG,
				// "Got day");
				
				gcal.set(gcal.get(Calendar.YEAR),
						gcal.get(Calendar.MONTH),
						gcal.get(Calendar.DAY_OF_MONTH));
				
				// Since time is currently set to midnight moves it ahead to the
				// class start time
				gcal.add(Calendar.HOUR,
						startTimeHour.intValue());
				gcal.add(Calendar.MINUTE,
						startTimeMinute.intValue());
				
				// sets when class in calander format
				when.setTimeInMillis(gcal.getTimeInMillis());
				
				// If alertTime is before the current alert moves on to the next
				// one.
				if (when.getTimeInMillis() > Calendar.getInstance()
						.getTimeInMillis()) {
					Log.d(RingerIntent.TAG,
							"making PI");
					pi = this.ringerIntentFinish(courseID,
							endTimeHour.intValue(),
							endTimeMinute.intValue());
					
					this.mAlarmManager.set(AlarmManager.RTC_WAKEUP,
							when.getTimeInMillis(),
							pi);
					
					Log.d(RingerIntent.TAG,
							"ringerChangeSetUp");
					break;
				}
				
			}
			gcal.add(Calendar.DAY_OF_MONTH,
					1);
		}
		
	}
	
	/**
	 * Loops threw and gets the extact day to set up intent on.
	 * 
	 * @param courseEndDate
	 * @param startTimeHour
	 * @param startTimeMinute
	 * @param endTimeHour
	 * @param endTimeMinute
	 * @param when
	 * @param gcal
	 * @param daysOfClass
	 * @param courseID
	 * @param overrideTime
	 * @param isOverrideTime
	 * @param holidayStartTime
	 * @param holidayEndTime
	 * @param isHoliday
	 */
	private void getsetRingerIntentDays(final Long courseEndDate,
			final Integer startTimeHour,
			final Integer startTimeMinute,
			final Integer endTimeHour,
			final Integer endTimeMinute,
			final Calendar when,
			final GregorianCalendar gcal,
			final List<Integer> daysOfClass,
			final long courseID,
			final Calendar overrideTime,
			final boolean isOverrideTime,
			final Calendar holidayStartTime,
			final Calendar holidayEndTime,
			final boolean isHoliday) {
		
		PendingIntent pi;
		
		while (gcal.getTimeInMillis() < courseEndDate.longValue()) {
			
			// if the class is on a given day it sets everything up and then
			// checks
			// to see if the alert is before it is currently set up.
			if (daysOfClass.contains(Integer.valueOf(gcal.get(Calendar.DAY_OF_WEEK)))) {
				
				gcal.set(gcal.get(Calendar.YEAR),
						gcal.get(Calendar.MONTH),
						gcal.get(Calendar.DAY_OF_MONTH));
				
				// Since time is currently set to midnight moves it ahead to the
				// class start time
				gcal.set(Calendar.HOUR,
						startTimeHour.intValue());
				gcal.set(Calendar.MINUTE,
						startTimeMinute.intValue());
				
				if ((overrideTime.getTimeInMillis() < gcal.getTimeInMillis())
						&& isOverrideTime) {
					this.setRingerChangeIntent(endTimeHour,
							endTimeMinute,
							when,
							courseID);
					break;
				}
				
				// sets when class in calander format
				when.setTimeInMillis(gcal.getTimeInMillis());
				
				// If alertTime is before the current alert moves on to the next
				// one.
				if ((holidayStartTime.getTimeInMillis() < gcal.getTimeInMillis())
						|| (holidayEndTime.getTimeInMillis() < gcal.getTimeInMillis())
						|| !isHoliday) {
					if (when.getTimeInMillis() > Calendar.getInstance()
							.getTimeInMillis()) {
						final Intent i = new Intent(this.mContext,
								OnAlarmReceiver.class);
						i.putExtra(IntentExtraStringStorage.TABLE_NAME,
								IntentExtraStringStorage.RINGER_CHANGE);
						i.putExtra(IntentExtraStringStorage.ROW_ID,
								courseID);
						i.putExtra(IntentExtraStringStorage.CLASS_END_HR,
								endTimeHour);
						i.putExtra(IntentExtraStringStorage.CLASS_END_MIN,
								endTimeMinute);
						i.putExtra(IntentExtraStringStorage.CLASS_BEGIN,
								true);
						
						int pendingIntentID;
						
						// error could happen if courseID exceeds that of int.
						pendingIntentID = Integer.MIN_VALUE
								+ (int) courseID;
						pi = PendingIntent.getBroadcast(this.mContext,
								pendingIntentID,
								i,
								PendingIntent.FLAG_ONE_SHOT);
						
						this.mAlarmManager.set(AlarmManager.RTC_WAKEUP,
								when.getTimeInMillis(),
								pi);
						
						Log.d(RingerIntent.TAG,
								"ringerChangeSetUp");
						break;
					}
					
				}
			}
			gcal.add(Calendar.DAY_OF_MONTH,
					1);
		}
		
	}
	
	/**
	 * XXX
	 * 
	 * @param when
	 * @param courseID
	 * @param endTimeHour
	 * @param endTimeMinute
	 * @return
	 */
	private PendingIntent ringerIntentFinish(final long courseID,
			final int endTimeHour,
			final int endTimeMinute) {
		
		final Intent i = new Intent(this.mContext,
				OnAlarmReceiver.class);
		i.putExtra(IntentExtraStringStorage.TABLE_NAME,
				IntentExtraStringStorage.RINGER_CHANGE);
		i.putExtra(IntentExtraStringStorage.ROW_ID,
				courseID);
		i.putExtra(IntentExtraStringStorage.CLASS_END_HR,
				endTimeHour);
		i.putExtra(IntentExtraStringStorage.CLASS_END_MIN,
				endTimeMinute);
		i.putExtra(IntentExtraStringStorage.CLASS_BEGIN,
				true);
		
		int pendingIntentID;
		
		// error could happen if courseID exceeds that of int.
		pendingIntentID = Integer.MIN_VALUE
				+ (int) courseID;
		final PendingIntent pi = PendingIntent.getBroadcast(this.mContext,
				pendingIntentID,
				i,
				PendingIntent.FLAG_ONE_SHOT);
		
		return pi;
		
	}
	
	/**
	 * set up ringer change
	 * 
	 * @param endTimeHour
	 *            end time hour
	 * @param endTimeMinute
	 *            end time minute
	 * @param when
	 *            when the class is
	 * @param courseID
	 *            course ID
	 */
	private void setRingerChangeIntent(final Integer endTimeHour,
			final Integer endTimeMinute,
			final Calendar when,
			final long courseID) {
		PendingIntent pi;
		pi = this.ringerIntentFinish(courseID,
				endTimeHour.intValue(),
				endTimeMinute.intValue());
		
		this.mAlarmManager.set(AlarmManager.RTC_WAKEUP,
				when.getTimeInMillis(),
				pi);
	}
	
	/**
	 * set up ringer change for a class
	 * 
	 * @param courseStartDate
	 *            start date of course
	 * @param courseEndDate
	 *            end date of course
	 * @param startTimeHour
	 *            start hour of course
	 * @param startTimeMinute
	 *            start minute of course
	 * @param endTimeHour
	 *            end hour of course
	 * @param endTimeMinute
	 *            end minute of course
	 * @param courseCursor
	 *            course cursor
	 */
	private void setUpRingerChange(final Long courseStartDate,
			final Long courseEndDate,
			final Integer startTimeHour,
			final Integer startTimeMinute,
			final Integer endTimeHour,
			final Integer endTimeMinute,
			final CursorCourse courseCursor) {
		final Calendar when = Calendar.getInstance();
		final GregorianCalendar gcal = new GregorianCalendar();
		final ContentValues contentValues = new ContentValues();
		final BackpackEduDatabaseHelper dbhelper = new BackpackEduDatabaseHelper(this.mContext);
		CursorSemester semesterCursor;
		CursorHoliday holidayCursor;
		CursorCourseTimeOverride overrideCursor;
		final int dayBitField = courseCursor.getColDayBitField();
		final List<Integer> daysOfClass = AllIntents.setMeetingDays(dayBitField);
		final long courseID = courseCursor.getColId();
		final Calendar overrideTime = Calendar.getInstance();
		final Calendar holidayEndTime = Calendar.getInstance();
		final Calendar holidayStartTime = Calendar.getInstance();
		boolean isOverRideTime = false;
		boolean isHoliday = false;
		
		contentValues.put(Semester.ID,
				Long.valueOf(courseCursor.getColSemesterId()));
		semesterCursor = dbhelper.getSemester(contentValues,
				null,
				true);
		semesterCursor.moveToFirst();
		
		holidayCursor = dbhelper.getHoliday(contentValues,
				new String[] { Holiday.END_DATE, Holiday.START_DATE },
				true);
		holidayCursor.moveToFirst();
		
		contentValues.put(Course.ID,
				Long.valueOf(courseCursor.getColId()));
		overrideCursor = dbhelper.getCourseTimeOverride(contentValues,
				new String[] {
						CourseTimeOverride.DATE,
						CourseTimeOverride.START_TIME_HOUR,
						CourseTimeOverride.START_TIME_MINUTE },
				true);
		gcal.set(gcal.get(Calendar.YEAR),
				gcal.get(Calendar.MONTH),
				gcal.get(Calendar.DAY_OF_MONTH));
		
		while (overrideCursor.isAfterLast() == false) {
			overrideTime.setTimeInMillis(overrideCursor.getColDate());
			overrideTime.set(Calendar.HOUR_OF_DAY,
					overrideCursor.getColStartTimeHour());
			overrideTime.set(Calendar.MINUTE,
					overrideCursor.getColStartTimeMinute());
			
			if (overrideTime.getTimeInMillis() > System.currentTimeMillis()) {
				isOverRideTime = true;
				break;
			}
			
			overrideCursor.moveToNext();
		}
		
		while (holidayCursor.isAfterLast() == false) {
			holidayEndTime.setTimeInMillis(holidayCursor.getColEndDate());
			
			if (holidayEndTime.getTimeInMillis() > System.currentTimeMillis()) {
				holidayStartTime.setTimeInMillis(holidayCursor.getColStartDate());
				isHoliday = true;
				break;
			}
			holidayCursor.moveToNext();
		}
		
		// checks to see if the current time is before the start date of the
		// class
		if (gcal.getTimeInMillis() < courseStartDate.longValue()) {
			gcal.setTimeInMillis(courseStartDate.longValue());
		}
		
		/*
		 * This loop goes threw and finds the next class in the future that class is on.
		 */
		
		this.getsetRingerIntentDays(courseEndDate,
				startTimeHour,
				startTimeMinute,
				endTimeHour,
				endTimeMinute,
				when,
				gcal,
				daysOfClass,
				courseID,
				overrideTime,
				isOverRideTime,
				holidayStartTime,
				holidayEndTime,
				isHoliday);
		
		semesterCursor.close();
		holidayCursor.close();
		overrideCursor.close();
		dbhelper.close();
	}
	
}
